Czego się nauczyliśmy?

Git jest dobrym wyborem jako system kontroli wersji. Żeby wygodnie go używać warto użyć usługi, która umożliwia hostowanie gitowych repozytoriów (projektów). Naturalnym wyborem jest Github.

Zadanie 1

Zarejestruj się na Githubie i stwórz repozytorium. Masz możliwość aplikowania o licencję studencką (m.in. darmowe prywatne repo).

Git

Instalacja Gita

Żeby skorzystać z Git trzeba go zainstalować.

Instrukcję można znaleźć tutaj.

¡Importante! Bez git nie ruszymy dalej. W razie trudności:

  1. Błąd, który Wam wyskakuje wrzucacie go Google.
  2. Znajdujecie możliwe rozwiązanie (najlepsze źródłoto Stack Overflow).
  3. Próbujecie jeszcze raz i w razie potrzeby wracacie do punktu 1.

W przypadku gdyby ktoś wpadł w nieskończoną pętlę proszę o kontakt.

Korzystanie z Gita

W praktyce, w większości przypadków możecie korzystać z Gita za pośrednictwem Waszego ulubionego IDE.

Dla R będzie to RStudio. Jeśli go nie macie to koniecznie zainstalujcie.

Jak widać możemy wygodnie (od lewej do prawej):

  • sprawdzić zmiany wprowadzone w plikach od ostatniego commita
  • zrobić commita
  • pobrać wersję z serwera (pull)
  • wysłać na serwer (push)
  • sprawdzić historie commitów
  • cofnąć zmiany, zignorować plik, otworzyć terminal (może być wygodne jeśli ktoś korzysta z Windowsa)
  • stworzyć nową gałąź
  • zmienić gałąź

Pod spodem można dodać pliki.

Jak wygląda typowa interakcja z Gitem?

  1. Chcemy dodać nową funkcjonalność/coś naprawić
  2. Zaczynamy od branch master
  3. Robimy pull (chcemy być zsynchronizowanie z serwerem).
  4. Tworzymy nowego brancha.
  5. Piszemy kod.

  1. Dodajemy pliki, które zmieniliśmy (Changes not staged for commit).
  2. Tworzymy commit-a
  3. Wysyłamy na serwer.

Dalej jest interakcja z Githubem.

  1. Klikamy w New pull request i wybieramy, którą gałąź chcemy zmergować.
  2. Pull request zostaje przez kogoś sprawdzony.
  3. Jak dostajemy okejkę to klikamy w Merge pull request
git init # tworzenei repo lokalnie
git pull # zmiany z serwera
git checkout -b new-branch # nowa gałąź
git add file1.R file2.py # staging plików
git commit -m 'commit message'
git push

Konflikty

Może się zdarzyć, że zmergowanie brancha nie jest możliwe, ponieważ występuje konflikt pomiędzy wersją naszą, a tą na serwerze. Najczęściej dlatego, że w międzyczasie ktoś inny wrzucił tam swoje zmiany. Wtedy te konflikty trzeba rozwiązać, dodać zmienione pliki.

git checkout -b new-branch # musimy być na nowej gałęzi
git merge master # próbujemy włączyć mastera
# rozwiazujemy konflikty lokalnie
git add conflict_file.py
git commit -m 'merging with master'
git push

Więcej na temat tego jak radzić sobie z branchami (gałęziami) można znaleźć tutaj.

gitignore

Tak jak nie ma sensu pakować wszystkich swoich rzeczy na wakacje, tak nie należy pakować wszystkich lokalnych plików do git-owego repozytorium. W szczególności należy unikać dodawania dużych plików (w razie konieczności należy skorzystać z dedykowanego rozwiązania).

Aby utrzymać porządek i nie dodać przypadkowo ważnych plików należy umieścić informację o tym co nie jest potrzebne w pliku gitignore.

raw_data/ #wykluczenie całego folderu
!raw_data/include_me/* # ale umożliwamuy dodawanie pików w wybranym jego podfolderze
*.html #wykluczenie wszystkich plików html
raport*.* # wykluczenie plikow ze slowem raport w nazwie

Synchronizacja lokalnego repo z tym na serwerze

Mamy dwie opcje:

  1. Możemy sklonować repozytorium znajdujące się na Githubie
git clone https://github.com/psobczyk/kurs_uam_2022.git
  1. Jeśli stworzyliśmy równocześnie repozytorium lokalnie i drugie na Githubie to trzeba je ze sobą połączyć. W tym celu loklanie okreslamy gdzie zdalnie (remote) się ono znajduje.
git remote -v # sprawdzamy jaki jest obecnie remote server

git remote add origin https://github.com/psobczyk/kurs_uam_2022.git

https czy ssh - o co w tym chodzi?

Jak zapewne zauważyliście na Githubie, klikając na klonowanie repozytorium mamy dwie opcje https i ssh. Pierwsza opcja oznacza, że wykonując wszelkie akcje na repozytorium będziemy się uwierzytelniać za pomocą loginu i hasła. Jest to dosyć niewygodne zwłaszcza przy dużej liczbie interakcji (i porządnym, długim haśle).

Druga opcja opiera się na użyciu kluczy gpg. Jest to bezpieczniejsza i wygodniejsza forma autoryzacji. W uproszczeniu: generuje się dwa klucze - prywatny i publiczny. Informację zakodowaną kluczem publicznym da się odczytać jedynie posiadając klucz prywatny. Klucz publiczny udostępnia się Githubowi, który jest dzięki temu w stanie sprawdzić czy my to rzeczywiście my (czyli czy posiadamy klucz prywatny).

Więcej na temat generowanie kluczy znajdziecie tutaj.

Proces dodawanie ich do konta jest opisany tutaj.

NIGDY

NIKOMU

NIE UDOSTĘPNIAMY

KLUCZA PRYWATNEGO

^^^ Podwójny gif pokazujący, że to jest naprawdę ważne.

Użyteczna ściągawka

Ściągawka do gita.

Zadanie 2

Sprawdź, że masz dostęp do terminala na swoim komputerze i wywołaj kilka prostych gitowych komend.

Rozwiązywanie zależności bibliotek

Omawialiśmy różne opcje w R i w Pythonie. Ale skupmy się na tych rozwiązaniach, które są przeze mnie sugerowane. Nie musicie się zgadzać :)

R

Używamy renv. Kropka. Przy tworzeniu nowego projeku w RStudio możemy kliknąć, że chcemy użyć renv-a. Efektem jest utworzenie wirtualnego środowiska, czyli nie korzystamy z pakietów eRowych wcześniej zainstalowanych a mamy do dyspozycji coś co zachowuje się jak świeża, czysta instalacja eRa.

Typowy workflow

library(renv)

##
# Tutaj instalujemy potrzebne nam pakiety
##
renv::snapshot() # tworzy lock file (podobny do pythonowego Pipfile.lock)
# dalej rozwijamy projekt
# instalując kolejne pakiety

renv::restore() # jeśli coś przestało działać wracamy do ostatniej wersji

renv::snapshot() # jeśli wszystko działa to zapamię†ujemy ostatnie wersje pakietów

Renv lockfile

Dostajmy plik, który wygląda mniej więcej tak:

{
  "R": {
    "Version": "4.0.5",
    "Repositories": [
      {
        "Name": "CRAN",
        "URL": "https://cran.rstudio.com"
      }
    ]
  },
  "Packages": {
    "renv": {
      "Package": "renv",
      "Version": "0.13.2",
      "Source": "Repository",
      "Repository": "CRAN",
      "Hash": "079cb1f03ff972b30401ed05623cbe92"
    },
    "rmarkdown": {
      "Package": "rmarkdown",
      "Version": "2.11",
      "Source": "Repository",
      "Repository": "CRAN",
      "Hash": "320017b52d05a943981272b295750388"
    }
  }
}

Do systemu kontroli wersji wrzucamy:

  • renv.lock
  • .Rprofile
  • renv/activate.R.

Uwaga

renv wspiera nie tylko CRANa. Jest dostępny Bioconductor, można instalować pakiety prosto z Githuba. Należy dodać resztę folderu `renv`` do pliku .gitignore.

Przywracanie środowiska

renv::init() # jesli nie zrobiło sie tego wcześniej
renv::restore()

Więcej informacji tutaj

Python

Sugerowane podejście to użycie trójcy pip+venv+pip-tools.

Środowisko wirtualne

Python domyślnie, w porównaniu z eRem, ma możliwość tworzenia wirtualnych środowisk (virtual environment). Jest to bardzo wygodna warstwa abstrakcji, która pozwala odizolować środowiska, w których działają poszczególne projekty. Dobre IDE domyślnie tworzą środowiska wirtualne dla nowych projektów. Jest to dobrą praktykę i nie należy robić inaczej.

Jeśli nie macie doświadczenia z wirtualnymi środowiskami to zachęcam mocno do przeczytania tego materiału.

Typowy workflow w terminalu dla pip + venv + pip-tools.

  1. Tworzymy nowe środowisko. Dobre miejsce to ukryty folder .venv w naszym głownym folderze.
python3.8 -m venv .venv

Tak jak pisałem wyżej, IDE może to zrobić na poziomie inicjalizacji projektu.

  1. Aktywujemy środowisko
source .venv/bin/activate

Teraz w naszym terminalu Python będzie korzystać domyślnie z naszego wirtualnego środowiska. To jest cecha konkretnej otwartej konsoli a nie systemu. Nowo otwarta konsola nie będzie wskazywać na to środowisko wirtualne. Możemy sprawdzić jaka jest ścieżka pod którą Python będzie szukać bibliotek

echo $PATH
  1. Instalujemy pip-toolsy
pip install -U pip setuptools wheel
  1. Określamy plik requirements.in
pandas
numpy
sqlalchemy
  1. Tworzymy requirements z dokładnymi wersjami bibliotek
pip-compile requirements.in
  1. Instalujemy biblioteki
pip install -r requirements.txt
  1. Ewentualnie wyłączenie środowiska wirtualnego
deactivate

Praca z systemem kontroli wersji

Do gita wrzucamy dwa pliki:

  • requirements.in (czyli nasze wymagania)
  • requirements.txt (czyli to co pip-compile udało się wywnioskować z drzewa zależności)

Achtung!

Należy dodać folder .venv do pliku .gitignore.

Po ściągnięciu repozytorium, jeśli nie istnieje to tworzymy środowisko wirtualne, a potem instalujemy pakiety komendą

pip install -r requirements.txt

W praktyce występuje kilka problemów:

  • czasem wynik działania pip-compile jest nieprawidłowy i trzeba mu pomóc dodając dodatkowe ograniczenia w pliku requirements.in (porównaj tu i tu]).
  • wsparcie dla tworzenia zależności dla wielu środowisk (testowe, devowe, produkcyjny) (por. ticket)

Czasami rozwiązanie drzewa zalezności wymaga ręcznych manipulacji. O różnych problemach na jakie można napotkać przeczytacie tutaj.

Python - praca w IDE

Dobre IDE mają oczywiście wsparcie dla Gita, ale nie oznacza to, że jesteśmy w stanie żyć zupełnie bez komend w terminalu. Samo ,,flow gitowe" czy stworzenie środowiska wirtualnego możemy tam zrobić. Ale rozwiązanie drzewa zależność, instalację pakietów z pliku requirements.txt robimy już z konsoli.

Zadanie 3

  1. Stwórz repozytorium na githubie.
  2. Zainicjuj lokalnie nowy projekt w swoim ulubionym IDE.
  3. Określ zależności (kilka bibliotek, które zwykle używasz pandas, numpy, dplyr, ggplot2 etc), wygeneruj ich dokładne wersje i zrób commita.
  4. Utwórz nowy projekt klonując repozytorium innej osoby. Stwórz nowe środowisko na podstawie pliku z lockiem.